Add filter infrastructure plus two filters for name and position.
authorrobertl <robertl@f51c46e8-681c-474f-0cfe-069cfd0219fb>
Fri, 7 Feb 2003 07:27:20 +0000 (07:27 +0000)
committerrobertl <robertl@f51c46e8-681c-474f-0cfe-069cfd0219fb>
Fri, 7 Feb 2003 07:27:20 +0000 (07:27 +0000)
Thanx, Alex!

gpsbabel/Makefile
gpsbabel/README
gpsbabel/defs.h
gpsbabel/filter_vecs.c [new file with mode: 0644]
gpsbabel/gpx.c
gpsbabel/main.c
gpsbabel/position.c [new file with mode: 0644]

index 0898812e37eb67ffed0e55148c14f1a02cf7855e..147ee0414047c64dcb6e89ad0eb81ba22f9111d0 100644 (file)
@@ -6,6 +6,8 @@ FMTS=magproto.o gpx.o geo.o gpsman.o mapsend.o mapsource.o \
        psp.o mxf.o holux.o garmin.o ozi.o tmpro.o dna.o tpg.o gpsdrive.o \
        xcsv.o xmapwpt.o gcdb.o
 
+FILTERS=position.o duplicate.o
+
 JEEPS=jeeps/gpsapp.o jeeps/gpscom.o \
        jeeps/gpsmath.o jeeps/gpsmem.o  \
        jeeps/gpsprot.o jeeps/gpsread.o \
@@ -16,8 +18,8 @@ JEEPS=jeeps/gpsapp.o jeeps/gpscom.o \
 
 COLDSYNC=coldsync/util.o coldsync/pdb.o
 
-OBJS=main.o queue.o route.o waypt.o util.o vecs.o mkshort.o csv_util.o \
-       $(COLDSYNC) $(GARMIN) $(JEEPS) $(FMTS)
+OBJS=main.o queue.o route.o waypt.o filter_vecs.o util.o vecs.o mkshort.o csv_util.o \
+       $(COLDSYNC) $(GARMIN) $(JEEPS) $(FMTS) $(FILTERS)
 
 .c.o:
        $(CC) -c $(CFLAGS) $< -o $@
@@ -94,6 +96,8 @@ tpg.o: tpg.c defs.h queue.h jeeps/gpsmath.h jeeps/gps.h jeeps/gpsport.h \
   jeeps/gpsproj.h jeeps/gpsnmeafmt.h jeeps/gpsnmeaget.h
 util.o: util.c defs.h queue.h
 vecs.o: vecs.c defs.h queue.h
+filter_vecs.o: filter_vecs.c defs.h queue.h
+position.o:position.c defs.h
 waypt.o: waypt.c defs.h queue.h
 xcsv.o: xcsv.c defs.h queue.h csv_util.h
 xmapwpt.o: xmapwpt.c defs.h queue.h csv_util.h
index 578eaaa94f9a75798be96b80ab32d7e9ce6d39f0..1138b4e67cdbc616278986c3c9e12ee3eb457f98 100644 (file)
@@ -292,6 +292,51 @@ THE FORMATS
        http://vip.hyperusa.com/~dougs/geocachingdb/geocachingdb.htm
 
 
+DATA FILTERS
+
+       GPSBabel supports data filtering.  Data filters are invoked from
+       the command line via the '-x' option.  It should be noted that
+       data filters are invoked in the order they appear on the command
+       line and can be used in intermittently between several variations
+       of input and output functions.  It should also be noted that
+       filtering data from different input types can sometimes produce
+       undesirable results due to differences in the native data formats.
+
+
+    POSITION
+
+       The position filter is designed to remove points based on their
+       proximity to each other.  Distances can be passed on the command 
+       line by passing the distance=XXX option to the filter.  Distance
+       options may be expressed in feet (distance=3f) or meters 
+       (distance=1m).  The default is zero feet, essentially a duplicate
+       position.
+
+       For example:
+
+       gpsbabel -i geo -f 1.loc -f 2.loc -x position,distance=1f \
+                -o mapsend -F 3.wpt
+
+       would remove multiple points that are within 1 foot of each other,
+       leaving just one.
+
+
+    DUPLICATE
+
+       The duplicate filter is designed to remove duplicate points based
+       on their shortname (traditionally a waypoint's name on the GPS
+       receiver), and/or their location (to a precision of 6 decimals).
+       This filter supports two options, "shortname" and "location".
+       Generally, at least one of these options is REQUIRED.  For example:
+
+       gpsbabel -i gpx -f 1.gpx -f 2.gpx -x duplicate,location,shortname \
+                -o gpx -F merged_with_no_dupes.gpx
+
+       would remove points that have duplicate shortnames *AND* duplicate
+       locations.  The result would be a GPX file that more than likely
+       contains only unique points and point data.
+
+
 COMMON USAGE
 
        Invocation was meant to be flexible.   Unfortunately, that can
index 69f9ec7a129941c9b121fceba0e3f290f98e7a71..02b67f02b1ba58018b1c025e225f06a5d202cb44 100644 (file)
@@ -174,6 +174,10 @@ typedef void (*ff_read) (void);
 typedef void (*ff_write) (void);
 char * get_option(const char *iarglist, const char *argname);
 
+typedef void (*filter_init) (char const *);
+typedef void (*filter_process) (void);
+typedef void (*filter_deinit) (void);
+
 void fprintdms(FILE *, const coord *, int);
 
 typedef void (*waypt_cb) (const waypoint *);
@@ -218,6 +222,12 @@ typedef struct ff_vecs {
        arglist_t *args;
 } ff_vecs_t;
 
+typedef struct filter_vecs {
+       filter_init f_init;
+       filter_process f_process;
+       filter_deinit f_deinit;
+} filter_vecs_t;
+
 void waypt_init(void);
 void route_init(void);
 void waypt_disp(const waypoint *);
@@ -226,11 +236,16 @@ void fatal(const char *, ...)
        __attribute__ ((__format__ (__printf__, 1, 2)))
 #endif
        ;
+
 ff_vecs_t *find_vec(char *, char **);
 void disp_vecs(void);
 void disp_formats(void);
 void printposn(const coord *c, int is_lat);
 
+filter_vecs_t * find_filter_vec(char *, char **);
+void disp_filters(void);
+void disp_filter_vecs(void);
+
 void *xcalloc(size_t nmemb, size_t size);
 void *xmalloc(size_t size);
 char *xstrdup(const char *s);
diff --git a/gpsbabel/filter_vecs.c b/gpsbabel/filter_vecs.c
new file mode 100644 (file)
index 0000000..f4c0c2e
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+    Describe vectors containing filter operations.
+    Copyright (C) 2002 Robert Lipe, robertlipe@usa.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
+
+ */
+
+#include <stdio.h>
+#include "defs.h"
+
+typedef struct {
+       filter_vecs_t *vec;
+       const char *name;
+       const char *desc;
+} fl_vecs_t;
+
+extern filter_vecs_t position_vecs;
+extern filter_vecs_t duplicate_vecs;
+
+static
+fl_vecs_t filter_vec_list[] = {
+       {
+               &position_vecs, 
+               "position",
+               "Remove Points Within Distance",
+       }, 
+       {
+               &duplicate_vecs, 
+               "duplicate",
+               "Remove Duplicates",
+       }, 
+        {
+               NULL,
+               NULL,
+               NULL
+       }
+};
+
+filter_vecs_t *
+find_filter_vec(char *const vecname, char **opts)
+{
+       fl_vecs_t *vec = filter_vec_list;
+       char *v = xstrdup(vecname);
+       char *svecname = strtok(v, ",");
+
+       while (vec->vec) {
+               if (strcmp(svecname, vec->name) == 0) {
+                       char * res = strchr(vecname, ',');
+                       if (res)
+                               *opts = strchr(vecname, ',')+1;
+                       else
+                               *opts = NULL;
+                       free(v);
+                       return vec->vec;
+               }
+               vec++;
+       }
+       free(v);
+       return NULL;
+}
+
+/*
+ *  Display the available formats in a format that's easy for humans to
+ *  parse for help on available command line options.
+ */
+void
+disp_filter_vecs(void)
+{
+       fl_vecs_t *vec;
+       for (vec = filter_vec_list; vec->vec; vec++) {
+               printf("        %-20.20s  %-50.50s\n",
+                       vec->name, vec->desc);
+       }
+}
+
+/*
+ *  Display the available formats in a format that's easy to machine
+ *  parse.   Typically invoked by programs like graphical wrappers to
+ *  determine what formats are supported.
+ */
+void
+disp_filters(void)
+{
+       fl_vecs_t *vec;
+       for (vec = filter_vec_list; vec->vec; vec++) {
+               printf("%s\t%s\n", vec->name, vec->desc);
+       }
+}
index 46b3ac25b5a2b54166a42de52e337a9da359c2db..c77361e028579296964916e526d637ee1ad88bae 100644 (file)
@@ -625,6 +625,9 @@ gpx_write_time(const time_t timep)
 {
        struct tm *tm = gmtime(&timep);
        
+       if (!tm)
+               return;
+       
        fprintf(ofd, "<time>%02d-%02d-%02dT%02d:%02d:%02dZ</time>\n",
                tm->tm_year+1900, 
                tm->tm_mon+1, 
index 116d4fc3c3a9f7baf5afa41025a9f5214df36fd9..35dd1c2932ffb3a23ea63df234aee7ede2060a74 100644 (file)
@@ -55,6 +55,8 @@ usage(const char *pname)
        );
 
        disp_vecs();
+       printf("\nSupported data filters:\n");
+       disp_filter_vecs();
 }
 
 
@@ -66,10 +68,12 @@ main(int argc, char *argv[])
        int argn;
        ff_vecs_t *ivecs = NULL;
        ff_vecs_t *ovecs = NULL;
+       filter_vecs_t *fvecs = NULL;
        char *fname = NULL;
        char *ofname = NULL;
        char *ivec_opts = NULL;
        char *ovec_opts = NULL;
+       char *fvec_opts = NULL;
 
        global_opts.objective = wptdata;
 
@@ -140,6 +144,18 @@ main(int argc, char *argv[])
                        case 'r':
                                global_opts.objective = rtedata;
                                break;
+                       case 'x':
+                               optarg = argv[argn][2]
+                                       ? argv[argn]+2 : argv[++argn];
+
+                               fvecs = find_filter_vec(optarg, &fvec_opts);
+
+                               if (fvecs) {
+                                       fvecs->f_init(fvec_opts);
+                                       fvecs->f_process();
+                                       fvecs->f_deinit();
+                               } 
+                               break;
                        case 'D':
                                optarg = argv[argn][2]
                                        ? argv[argn]+2 : argv[++argn];
@@ -148,6 +164,9 @@ main(int argc, char *argv[])
                        case '^':
                                disp_formats();
                                exit(0);
+                       case '%':
+                               disp_filters();
+                               exit(0);
                        case 'h':
                        case '?':
                                usage(argv[0]);
diff --git a/gpsbabel/position.c b/gpsbabel/position.c
new file mode 100644 (file)
index 0000000..ef33137
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+    Distance Between Points Filter
+
+    Copyright (C) 2002 Robert Lipe, robertlipe@usa.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
+
+ */
+#include <stdio.h>
+#include <math.h>
+#include "defs.h"
+
+#ifndef M_PI
+#  define M_PI 3.14159265358979323846
+#endif
+
+extern queue waypt_head;
+
+static double pos_dist;
+
+static double
+gc_distance(double lat1, double lon1, double lat2, double lon2)
+{
+       double rlat1, rlat2, rlon1, rlon2;
+
+       /* convert to radians */
+       rlat1 = (lat1 * M_PI) / 180.0;
+       rlon1 = (lon1 * M_PI) / 180.0;
+       rlat2 = (lat2 * M_PI) / 180.0;
+       rlon2 = (lon2 * M_PI) / 180.0;
+
+       return (acos((sin(rlat1) * sin(rlat2)) +
+               (cos(rlat1) * cos(rlat2) * cos(rlon1 - rlon2))));
+}
+
+static int
+position_comp(const void * a, const void * b)
+{
+       const waypoint *x1 = *(waypoint **)a;
+       const waypoint *x2 = *(waypoint **)b;
+       double latdiff, londiff, max;
+
+       /*
+        * this compare makes the assumption that things will fall into
+        * order by declaring their biggest single axial difference.
+        * It is much less math than distance and bearing from some random
+        * point.
+        */
+
+       londiff = (x1->position.longitude.degrees -
+                  x2->position.longitude.degrees) * 1000000.0;
+       latdiff = (x1->position.latitude.degrees -
+                  x2->position.latitude.degrees) * 1000000.0;
+
+       max = fabs(londiff) >= fabs(latdiff) ? floor(londiff) : floor(latdiff);
+
+       if (max < 0)
+               return (-1);
+       else if (max > 0)
+               return (1);
+
+       return(0);
+}
+
+void
+position_process(void)
+{
+       queue * elem, * tmp;
+       waypoint ** comp;
+       double dist;
+       int i, wc;
+
+       wc = waypt_count();
+
+       comp = xcalloc(wc, sizeof(*comp));
+
+       i = 0;
+
+       QUEUE_FOR_EACH(&waypt_head, elem, tmp) {
+               comp[i] = (waypoint *)elem;
+               i++;
+       }
+
+       qsort(comp, wc, sizeof(waypoint *), position_comp);
+
+       for (i = 0 ; i < (wc - 1) ; i++) {
+               dist = gc_distance(comp[i]->position.latitude.degrees,
+                                  comp[i]->position.longitude.degrees,
+                                  comp[i+1]->position.latitude.degrees,
+                                  comp[i+1]->position.longitude.degrees);
+
+               /* convert radians to integer feet */
+               dist = (int)((((dist * 180.0 * 60.0) / M_PI) / 1.1516) * 5280.0);
+
+               if (dist <= pos_dist) 
+                       waypt_del(comp[i]);
+       }
+
+       if (comp)
+               free (comp);
+}
+
+void
+position_init(const char *args) {
+       char *fm;
+       const char *p;
+
+       p = get_option(args, "distance");
+
+       if (p) {
+               pos_dist = strtod(p, &fm);
+
+               if ((*fm == 'm') || (*fm == 'M')) {
+                        /* distance is meters */
+                       pos_dist *= 3.2802;
+               }
+       }
+}
+
+void
+position_deinit(void) {
+}
+
+filter_vecs_t position_vecs = {
+       position_init,
+       position_process,
+       position_deinit
+};